package cn.funeralobjects.upms.controller;

import cn.funeralobjects.common.service.*;
import cn.funeralobjects.common.web.controller.*;
import cn.funeralobjects.common.web.controller.converter.ModelMapper;
import cn.funeralobjects.upm.web.model.MUser;
import cn.funeralobjects.upm.web.model.VUser;
import cn.funeralobjects.upm.web.model.VUserCertificate;
import cn.funeralobjects.upm.web.model.VUserSecret;
import cn.funeralobjects.upms.api.UserApi;
import cn.funeralobjects.upms.controller.converter.UserCertificateMapper;
import cn.funeralobjects.upms.controller.converter.UserCertificateTypePropertyEditor;
import cn.funeralobjects.upms.controller.converter.UserMapper;
import cn.funeralobjects.upms.entity.EUser;
import cn.funeralobjects.upms.enums.UserCertificateType;
import cn.funeralobjects.upms.model.User;
import cn.funeralobjects.upms.model.UserCertificate;
import cn.funeralobjects.upms.model.UserSecret;
import cn.funeralobjects.upms.service.UserCertificateService;
import cn.funeralobjects.upms.service.UserSecretService;
import cn.funeralobjects.upms.service.UserService;
import cn.funeralobjects.util.ReactorUtils;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 用户外部访问接口
 *
 * @author FuneralObjects
 * Create date: 2020/4/27 11:17 AM
 */
@RestController
@RequestMapping(name = "user", path = "/user")
public class UserController implements UserApi,

        CommonModifyController<VUser, MUser, User, EUser, Integer>,
        RestCodeController<VUser, EUser, Integer>,
        RestEntityController<VUser, EUser, Integer>,
        RestEnableController<VUser, EUser, Integer>,
        RestCommitController<Integer> {

    @Resource
    private UserSecretService userSecretService;

    @Resource
    private UserCertificateMapper userCertificateMapper;

    @Resource
    private UserService userService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserCertificateService userCertificateService;


    @Override
    public ModelMapper<MUser, User, EUser, VUser> modelMapper() {
        return userMapper;
    }

    @Override
    public CommonModifyService<User, EUser, Integer> commonModifyService() {
        return userService;
    }

    @Override
    public CodeService<EUser, Integer> codeService() {
        return userService;
    }

    @Override
    public CommitService<Integer> commitService() {
        return userService;
    }

    @Override
    public EnableService<EUser, Integer> enableService() {
        return userService;
    }

    @Override
    public EntityService<EUser, Integer> entityService() {
        return userService;
    }

    @Override
    public IdService<Integer> idService() {
        return userService;
    }

    @PostMapping("/{userId}/cert")
    @Override
    public Mono<VUserCertificate> addUserCert(@PathVariable("userId") Integer userId,
                                              @RequestParam("code") String code,
                                              @RequestParam("type") UserCertificateType type,
                                              @RequestParam("cert") String cert) {
        return Mono.just(userId)
                .map(id -> userCertificateService.add(new UserCertificate().setUserId(id).setCode(code).setType(type).setCert(cert)))
                .map(userCertificateMapper::toVo);
    }

//    @PostMapping("/code-{userCode}/cert")
//    @Override
//    public Mono<VUserCertificate> addUserCert(@PathVariable("userCode") String userCode,
//                                              @RequestParam("code") String code,
//                                              @RequestParam("type") UserCertificateType type,
//                                              @RequestParam("cert") String cert) {
//        return Mono.just(userCode)
//                .map(userService::findByCode)
//                .filter(Optional::isPresent)
//                .switchIfEmpty(Mono.error(() -> new LackDataException(String.format("The User[%s] doesn't exists", userCode), userService.createDataCode("code", userCode))))
//                .map(Optional::get)
//                .map(user -> userCertificateMapper.toVo(userCertificateService.add(new UserCertificate().setCert(cert).setType(type).setCode(code).setUserId(user.getId()))));
//    }

    @GetMapping("/{userId}/cert")
    @Override
    public Mono<List<VUserCertificate>> getUserCerts(@PathVariable Integer userId) {
        return Mono.just(userId)
                .map(userCertificateService::findAllByUserId)
                .map(list -> list.stream().map(userCertificateMapper::toVo).collect(Collectors.toList()));
    }

    @PatchMapping("/{userId}/secret")
    @Override
    public Mono<Void> updateSecret(@PathVariable("userId") Integer userId,
                                   @RequestParam("secret") String secret) {
        return Mono.just(userId)
                .flatMap(ReactorUtils.thenEmpty(id -> userSecretService.add(new UserSecret().setSecretKey(secret).setUserId(id))));
    }

//    @PatchMapping("/code-{userCode}/secret")
//    @Override
//    public Mono<Void> updateSecret(@PathVariable("userCode") String userCode,
//                                   @RequestParam("secret") String secret) {
//        return Mono.just(userCode)
//                .map(userService::findByCode)
//                .filter(Optional::isPresent)
//                .switchIfEmpty(Mono.error(() -> new LackDataException(String.format("The User[%s] doesn't exists", userCode), userService.createDataCode("code", userCode))))
//                .map(Optional::get)
//                .flatMap(ReactorUtils.thenEmpty(user -> userSecretService.add(new UserSecret().setSecretKey(secret).setUserId(user.getId()))));
//
//    }

    @GetMapping("/code-{userCode}")
    @Override
    public Mono<VUserSecret> getSecret(@PathVariable("userCode") String userCode) {
        return Mono.just(userCode)
                .map(userService::findByCode)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(user -> userSecretService.findById(user.getId()))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(e -> new VUserSecret().setUserId(e.getUser().getId()).setSecretKey(e.getSecretKey()));
    }

    @GetMapping("/cert")
    @Override
    public Mono<VUserSecret> getSecret(@RequestParam("cert") String cert, @RequestParam("type") UserCertificateType type) {
        return Mono.just(type)
                .map(t -> userCertificateService.findCertificate(cert, t))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(certificate -> userSecretService.findById(certificate.getUser().getId()))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .map(e -> new VUserSecret().setUserId(e.getUser().getId()).setSecretKey(e.getSecretKey()));
    }

    @InitBinder
    private void initBinder(WebDataBinder dataBinder) {
        dataBinder.registerCustomEditor(UserCertificateType.class, new UserCertificateTypePropertyEditor());
    }


}
